Release 0.3.1: signup, subdomains dataset, Authlib OAuth - #17
Merged
Conversation
Authlib 1.8.0 (2026-08-30) ships httpx2 support, so PKCE challenge generation, the authorization URL, code exchange, and refresh now go through its OAuth2Client instead of hand-rolled request building. The point is to stop owning protocol code: Authlib maintains RFC 6749/7636 handling, we keep only what it lacks. What stays ours, deliberately: - DiscolikeAuth (httpx2.Auth): single 401 replay, cross-process credential reload (PropelAuth revokes the whole refresh family on a replayed refresh token, so two CLI processes must not both refresh), sync+async locks, and the API-key branch. Authlib's sync client has no lock and refreshes on expiry only. - Dynamic client registration and metadata discovery: Authlib has no RFC 7591 client (authlib/authlib#526, open since 2023). TokenClient/AsyncTokenClient override parse_response_token so error types, status codes, and token-redacted payloads are unchanged for callers. Refreshes use a dedicated token-endpoint client (30s timeout) rather than the SDK http_client, so proxies configured there no longer reach the token endpoint; noted in the changelog. Verified live against prod: login (reused DCR client), status, usage, proactive refresh with token rotation, and 401 replay. Also: README sign-up links to discolike.com/signup.
refactor(sdk): hand OAuth token requests to Authlib 1.8.0 (0.3.1)
Regenerated request models from the platform spec: AppendParams.dataset now allows "subdomains" (up to 300 known subdomains per domain).
Module-level rather than a client method because the client requires a credential at construction and signup is the one call that has none. Maps 409 to ValidationError since raise_for_status had no branch for it and account-exists is a validation-style failure, not a generic error.
The only command that works before auth login, on purpose: it is how an agent opens the account its human will later log into.
Without this, an agent (or a bug in one) could loop discolike signup / discolike.signup() over arbitrary emails from one machine with nothing in the way. Remembering the last signed-up email and requiring an explicit override before switching adds friction to that specific failure mode without touching normal single-account use.
A person with no account was bounced straight into a browser OAuth flow that has nothing to authenticate against. Asking up front and routing "no" into the same signup path discolike signup already uses avoids that dead end without touching the existing login flow for everyone who already has a key or credential.
A bad name currently only fails after a round trip to the API. Mirroring PropelAuth's real rule client-side (probed and pinned in the API task) means an agent gets the rejection immediately, with the same wording, instead of waiting on a network call to learn the same thing.
The interactive account question only checked the login subcommand's own --api-key, so `discolike --api-key X auth login` still got asked and, on "no", discarded a key that was already good. It needed the same option-source check the API-key login path itself already uses. Also dedupes _is_interactive, which had drifted into two copies.
get_parameter_source for "method" was read off the root Click context, which only tracks root-level options, so a --method passed to the login subcommand was never detected as explicit and the account question still fired for `auth login --method api_key`. Read it off the subcommand's own context instead. Also validate --method before offering signup, so an invalid method errors immediately instead of asking the account question first. Restores an assertion dropped from test_login_forged_error_callback_cannot_evict_stored_client during an earlier commit on this branch.
The punctuation whitelist rejected real names PropelAuth itself accepts (digits, parentheses, underscores, an email-shaped name). Its only actual requirement is length; ours now adds just the two things worth blocking client-side before a round trip: no angle-bracket/control-character injection, and at least one letter.
CI type-checks the tests directories.
An injected http_client is transport only. A bare one had no base URL at all, and one pointed elsewhere would have taken the signup body with it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ships 0.3.1 to main.
discolike.signup()/async_signup()anddiscolike signupopen an account from a work email with no credential, so an agent can onboard a user instead of dead-ending on "get an API key".auth loginoffers it when no account exists.AppendParams.datasetacceptssubdomains.http_client=no longer reaches the token endpoint.517 tests pass, ruff clean.
Greptile Summary
The release adds credential-free SDK and CLI signup, support for the
subdomainsappend dataset, and Authlib-backed OAuth token handling.Confidence Score: 4/5
The PR is not yet safe to merge because credential saves and logout can erase the signup-email safeguard and allow a different-address signup without confirmation.
The remembered signup address shares the configuration file with credentials, but
save_credentialreplaces that file withoutsignup_emailanddelete_credentialdeletes it or retains only the OAuth registration, so ordinary login, refresh persistence, or logout removes the state used to require confirmation for a later address.Files Needing Attention: packages/discolike/src/discolike/_config.py
Important Files Changed
subdomains.Reviews (2): Last reviewed commit: "fix(sdk): signup always posts to base_ur..." | Re-trigger Greptile